﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using ZilLion.Service.Core.ConfigManager.Manager;
using ZilLion.Service.Core.Log;
using ZilLion.Service.Core.Snowflake;
using ZilLion.Service.Core.WebSocket.Interface;

namespace ZilLion.Service.Core.WebSocket
{
    /// <summary>
    ///     websocket 消息处理
    /// </summary>
    public abstract class WebSocketHandler : IWebSocketHandler
    {
        private readonly IMessagePersistenceHandle _persistenceHandle;

        /// <summary>
        ///     构造
        /// </summary>
        /// <param name="webSocketConnectionManager"></param>
        /// <param name="persistenceHandle"></param>
        protected WebSocketHandler(WebSocketConnectionManager webSocketConnectionManager,
            IMessagePersistenceHandle persistenceHandle)
        {
            _persistenceHandle = persistenceHandle;
            WebSocketConnectionManager = webSocketConnectionManager;
        }

        public WebSocketConnectionManager WebSocketConnectionManager { get; set; }


        public virtual async Task ReceiveAsync(System.Net.WebSockets.WebSocket socket, WebSocketReceiveResult result,
            string messageJson)
        {
            try
            {
                var message = JsonConvert.DeserializeObject<SocketMessage>(messageJson);

                if (message != null && message.MessageType.IsNotNullOrEmpty())
                {
                    var strategy = MessageHandleStrategyFactory.GetInstance()
                        .GetStrategyInstance(message.MessageType);
                    if (strategy == null)
                        await SendMessageAsync(socket,
                            new SocketMessage
                            {
                                MessageId = SnowflakeIdCreater.Instance().GetNextId().ToString(),
                                MessageType = SocketMessageType.STRATEGY_NOTFOUND,
                                Data = $"消息:{messageJson}找不到对应的处理策略"
                            });
                    else
                        strategy?.HandleMessage(message, socket, this);
                }
            }
            catch (Exception e)
            {
                ZilLionLogManager.Instance().Error(e);
                await SendMessageAsync(socket, new SocketMessage
                {
                    MessageType = SocketMessageType.SERVER_ERROR,
                    MessageId = SnowflakeIdCreater.Instance().GetNextId().ToString(),
                    Data =
                        $"消息:{messageJson} 接收时发生错误!{Environment.NewLine}错误消息为:{e.Message}{Environment.NewLine}错误堆栈为:{e.StackTrace}"
                }).ConfigureAwait(false);
            }
        }

        #region 链接操作

        /// <inheritdoc />
        public virtual async Task OnConnected(System.Net.WebSockets.WebSocket socket, string clientid)
        {
            if (clientid.IsNullOrEmpty())
            {
                await SendMessageAsync(socket, new SocketMessage
                {
                    MessageId = SnowflakeIdCreater.Instance().GetNextId().ToString(),
                    MessageType = SocketMessageType.CLINET_UNACCESS,
                    Data = "SOCKET授权失败!"
                }).ConfigureAwait(false);
                socket.Dispose();
                return;
            }
            WebSocketConnectionManager.AddSocket( socket);
            await SendMessageAsync(socket, new SocketMessage
            {
                MessageId = Guid.NewGuid().ToString("N"),
                MessageType = SocketMessageType.CLINET_CONNECTED,
                Data = WebSocketConnectionManager.GetId(socket)
            }).ConfigureAwait(false);
            if (ZilLionConfigurationManager.GetConfigInstance<WebSocketServiceConfig>().EnableMessagePersistence)
            {
                var id = WebSocketConnectionManager.GetId(socket);
                if (id.IsNotNullOrEmpty())
                {
                    var messages = _persistenceHandle?.GetMessage(id);
                    foreach (var message in messages ?? new List<SocketMessage>())
                    {
#pragma warning disable CS4014 // 由于此调用不会等待，因此在调用完成前将继续执行当前方法
                        SendMessageAsync(socket, message).ConfigureAwait(false);
#pragma warning restore CS4014 // 由于此调用不会等待，因此在调用完成前将继续执行当前方法
                    }
                }
            }
        }

        /// <inheritdoc />
        public virtual async Task OnDisconnected(System.Net.WebSockets.WebSocket socket)
        {
            var id = WebSocketConnectionManager.GetId(socket);
            await WebSocketConnectionManager.RemoveSocket(id)
                .ConfigureAwait(false);
            ZilLionLogManager.Instance().Trace($"{id}断开连接");
        }

        #endregion

        #region 消息发送

        /// <inheritdoc />
        public virtual async Task SendMessageAsync(System.Net.WebSockets.WebSocket socket, SocketMessage socketMessage)
        {
            if (socket.State != WebSocketState.Open)
            {
                if (ZilLionConfigurationManager.GetConfigInstance<WebSocketServiceConfig>().EnableMessagePersistence)
                    _persistenceHandle.SaveMessage(WebSocketConnectionManager.GetId(socket), socketMessage);
                return;
            }


            var serializedMessage = JsonConvert.SerializeObject(socketMessage);
            var encodedMessage = Encoding.UTF8.GetBytes(serializedMessage);
            await socket.SendAsync(new ArraySegment<byte>(encodedMessage,
                    0,
                    encodedMessage.Length),
                WebSocketMessageType.Text,
                true,
                CancellationToken.None).ConfigureAwait(false);
        }

        /// <inheritdoc />
        public virtual async Task SendMessageAsync(string socketId, SocketMessage socketMessage)
        {
            var socket = WebSocketConnectionManager.GetSocketById(socketId);
            if (socket != null)
            {
                await SendMessageAsync(socket, socketMessage)
                    .ConfigureAwait(false);
            }
            else
            {
                if (ZilLionConfigurationManager.GetConfigInstance<WebSocketServiceConfig>().EnableMessagePersistence)
                    _persistenceHandle.SaveMessage(socketId, socketMessage);
            }
        }

        /// <inheritdoc />
        public virtual async Task SendMessageToAllAsync(SocketMessage socketMessage)
        {
            foreach (var pair in WebSocketConnectionManager.GetAll())
                if (pair.Value.State == WebSocketState.Open)
                {
                    await SendMessageAsync(pair.Value, socketMessage).ConfigureAwait(false);
                }
                else
                {
                    if (ZilLionConfigurationManager.GetConfigInstance<WebSocketServiceConfig>()
                        .EnableMessagePersistence)
                        _persistenceHandle.SaveMessage(pair.Key, socketMessage);
                }
        }


        /// <inheritdoc />
        public virtual async Task SendMessageToGroupAsync(string groupId, SocketMessage socketMessage)
        {
            var sockets = WebSocketConnectionManager.GetAllFromGroup(groupId);
            if (sockets != null)
                foreach (var socket in sockets)
                    await SendMessageAsync(socket, socketMessage);
        }


        /// <inheritdoc />
        public virtual async Task SendMessageToGroupAsync(string groupId, SocketMessage socketMessage,
            IList<string> exceptgroupId)
        {
            var sockets = WebSocketConnectionManager.GetAllFromGroup(groupId);
            if (sockets != null)
                foreach (var id in sockets)
                    if (exceptgroupId.All(x => x != id))
                        await SendMessageAsync(id, socketMessage);
        }

        #endregion
    }
}